/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package jigdo.downloader;

import java.net.*;
import java.io.*;
import java.util.ArrayList;
import java.util.logging.Logger;
import jigdo.DiskIO.ProgressEntry;

import jigdo.JigdoException;

/**
 *
 * @author John R Sohn
 */
public class DownloadEntry extends Thread {
    
    
    /**
     * The matched progress entry.
     */
    public jigdo.DiskIO.ProgressEntry Progress;
    
    Logger logger = Logger.getLogger(this.getClass().getName());
    
    public void setParentLogger(Logger parent)
    {
          logger.setParent(parent);
          logger.setLevel(null);
    }

    /**
     * Retrieves the status of this download.
     *
     * @return
     */
    public DownloadStatus getDownloadStatus() {
        return Status;
    }
    
    private jigdo.DiskIO.ProgressEntry progress;
    
    public void setProgressEntry(ProgressEntry entry)
    {
        progress = entry;
        entry.Parent = this;
        
        if (entry.Written)
        {
            this.setDownloadStatus(DownloadStatus.Finished);
        }
    }

    /**
     * Sets the status of this download, should not be called.
     *
     * @param status
     */
    public void setDownloadStatus(DownloadStatus status) {
        synchronized (Status) {
            Status = status;
        }

        synchronized (listeners) {
            for (int x = 0; x < listeners.size(); x++) {
                listeners.get(x).StateChanged(this, Status);
            }
        }

    }
    /**
     * The image download task which owns this entry.
     */
    public ImageDownloadTask Parent;
    /**
     * The .jigdo file entry corresponding to this job.
     */
    public jigdo.data.jigdo.JigdoRemoteFileEntry JigdoEntry;
    /**
     * The template file entry corresponding to this job.
     */
    public jigdo.data.template.section.DESCEntries.TemplateDESCFileInfoEntry TemplateEntry;
    /**
     * The position of this entry in the owning template entries list.
     */
    public int OrdinalPosition;
    /**
     * Stores the number of bytes currently downloaded
     */
    private long Downloaded;

    /**
     * Sets the current progress of the download and fires the progress event of all listeners
     * @param size The size currently downloaded.
     */
    public void setDownloadedSize(long size) {
        Downloaded = size;

        synchronized (listeners) {
            for (int x = 0; x < listeners.size(); x++) {
                listeners.get(x).Progress(this, Downloaded);
            }
        }
    }

    /**
     * Returns the current number of bytes downloaded.
     * @return The number of bytes currently downloaded for this file.
     */
    long getDownloadedSize() {
        return Downloaded;
    }
    
    /**
     * If available from server, contains the size of the file.
     */
    public long ReportedSize;
    /**
     * If successful, contains the bytes downloaded from the server
     */
    public byte[] DownloadedBytes;
    /**
     * If an error is encountered, contains the exception caught
     */
    public Throwable LastError = null;
    /**
     * Reports the status of the download thread.
     */
    private DownloadStatus Status = DownloadStatus.NotStarted;
    /**
     * Indicates where the file is being stored currently.
     */
    public StorageStatus StoredAt = StorageStatus.NotStarted;
    Thread downloader;

    /**
     * Constructor which creates this entry and associates it's parent.
     *
     * @param parent The parent ImageDownloadTask which created this entry.
     */
    public DownloadEntry(ImageDownloadTask parent) {
        Parent = parent;
        listeners = new ArrayList<DownloadListener>();
    }

    /**
     * Pauses download.
     */
    public void Pause() {
        if (this.getDownloadStatus() == DownloadStatus.Downloading) {
            this.setDownloadStatus(DownloadStatus.Paused);
        }
    }

    public void StopDownload() {

        try {
            setDownloadStatus(DownloadStatus.Aborted);
            this.interrupt();
        } catch (Exception e) {
            this.setError("An error occurred while stopping the download task.", e);
        }
    }

    /**
     * Fires the error event of all registered listeners
     * @param message A human readable message
     * @param e The exception thrown.
     */
    void setError(String message, Exception e)
    {
        synchronized(listeners)
        {
            LastError =e;
            
            for (int x=0; x < listeners.size();x++)
            {
                listeners.get(x).Error(this, new JigdoException(message,LastError));
            }
        }
    }
    
    /** 
     * Initiates the download.
     */
    public void Download() {
        if (getState() == Thread.State.TERMINATED) {
            ;
            downloader.start();
        }
    }
    
    /**
     * The listeners registered with this object.
     */
    private java.util.ArrayList<DownloadListener> listeners;

    /**
     * Adds a download listener to the instance.
     * @param listener The class implementing the listener interface.
     */
    public void AddListener(DownloadListener listener) {
        synchronized (listeners) {
            listeners.add(listener);
        }
    }

    /**
     * The main download thread.
     */
    @Override
    public void run() {

        setDownloadStatus(DownloadStatus.StartingDownload);

        try {
            java.net.URL url =
                    new URL(Parent.BaseServerURL + JigdoEntry.URL);

            URLConnection conn = url.openConnection();


            if (getDownloadStatus() == DownloadStatus.Paused) {
                setDownloadStatus(DownloadStatus.Downloading);

                conn.setRequestProperty("Range", "bytes=" + Long.toString(getDownloadedSize()) + "-");
            }

            InputStream web = conn.getInputStream();

            setDownloadStatus(DownloadStatus.Downloading);

            ReportedSize = conn.getContentLength();
            DownloadedBytes = null;
            setDownloadedSize(0);
            java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();

            byte[] buffer = new byte[100000];


            while (getDownloadStatus() != DownloadStatus.Paused
                    && getDownloadStatus() != DownloadStatus.Stopping
                    && getDownloadStatus() != DownloadStatus.Aborted) {
                int size = web.read(buffer, 0, 100000);

                if (size == -1) {

                    synchronized (DownloadedBytes) {
                        setDownloadStatus(DownloadStatus.Stopping);

                        DownloadedBytes = out.toByteArray();

                        out.reset();

                        out = null;
                    }
                } else {

                    out.write(buffer, 0, size);
                    setDownloadedSize(size + getDownloadedSize());
                }



                if (getDownloadStatus() != DownloadStatus.Aborted
                        && getDownloadStatus() != DownloadStatus.Paused) {
                    java.io.ByteArrayInputStream infomd5 =
                            new java.io.ByteArrayInputStream(DownloadedBytes);

                    setDownloadStatus(DownloadStatus.Verifying);

                    String m64 = jigdo.utils.MD5Helpers.GetJigdoMD5(DownloadedBytes);

                    if (JigdoEntry.MD5.equals(m64)) {
                        setDownloadStatus(DownloadStatus.Downloaded);
                    } else {
                        DownloadedBytes = null;
                        setDownloadStatus(DownloadStatus.FailedMD5);
                    }
                }
            }
        } catch (Exception e) {

            setDownloadStatus(DownloadStatus.Error);
            setError("Error occurred during download",e);
        }

    }
}
